﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.IO;
using System.Reflection;
using System.Configuration;

namespace EmbeddedLanguage
{
    public class JSNamespace
    {
        public Assembly Assembly; 

        public string Path;

        public JSNamespace Parent;

        public List<JSNamespace> Children;

        public List<JSNamespace> ChildrenNN
        {
            get
            {
                if (Children == null) Children = new List<JSNamespace>();
                return Children;
            }
        }

        public List<JSFunction> Functions;

        public List<JSFunction> FunctionsNN
        {
            get
            {
                if (Functions == null) Functions = new List<JSFunction>();
                return Functions;
            }
        }

        //public List<Expression> Expressions;

        //public List<Expression> ExpressionsNN
        //{
        //    get
        //    {
        //        if (Expressions == null) Expressions = new List<Expression>();
        //        return Expressions;
        //    }
        //}

        public NestedString BuildJSStructure()
        {
            var ns = new NestedString
            {
                ChildSeparator = ",",
                ChildSeparatorFollowingLastElement = ";",
                LHS = "var ",
                Comment = "JSNamespace.BuildJSStructure",
            };
            this.NestedString = ns;
            Children.DoForEach(jsNS =>
            {
                jsNS.RenderJS(ns);
            });
            return ns;
        }

        public void Save()
        {
            string outputPath = ConfigurationManager.AppSettings["EmbeddedLanguage.OutputPath"];
            //string relativePath = ConfigurationManager.AppSettings["EmbeddedLanguage.RelativeToOutputPath"];
            if (outputPath == null)
            {
                IDetermineOutputFilePath FilePathDeterminer = null;
                string typeString = ConfigurationManager.AppSettings["EmbeddedLanguage.IDetermineOutputFilePath.TypeString"];
                if (typeString != null)
                {
                    Type typ = Type.GetType(typeString, true);
                    FilePathDeterminer = Activator.CreateInstance(typ) as IDetermineOutputFilePath;
                }
                else
                {
                    FilePathDeterminer = new DefaultOutputPath();
                }
                outputPath = FilePathDeterminer.GetOutputFolderPath(this.Assembly);
            }
            //if (relativePath != null)
            //{
            //    if (relativePath == "..\\..\\")
            //    {
            //    }
            //}
            DirectoryInfo dirInfo = new DirectoryInfo(outputPath);
            
            if (dirInfo.Exists)
            {
                dirInfo.Delete(true);
            }
            dirInfo.Create();
            string fileRoot = outputPath + "\\" + this.Assembly.FullName.SubstringBefore(",");
            string JSFilePath = fileRoot + ".js";
            var fi = new FileInfo(JSFilePath);
            var sw = fi.AppendText();
            sw.Write(this._JSOutput);
            sw.Close();
            string XMLFilePath = fileRoot + ".js.debug.xml";
            var fiXML = new FileInfo(XMLFilePath);
            var swXML = fiXML.AppendText();
            swXML.Write(this.NestedString.Xml);
            swXML.Close();
            

        }

        private string _JSOutput;
        
        public string ToJS()
        {
            var ns = this.BuildJSStructure();
            this._JSOutput = ns.GetString();
            return this._JSOutput;
        }

        public NestedString NestedString;

        public void RenderJS(NestedString ns)
        {
            string path = 
                this.Parent == null ? 
                    Path 
                :  
                    this.Parent.Path == null ?
                        Path
                    :
                        Path.SubstringAfter(this.Parent.Path)
                ;
            if(path.Contains(".")) path = path.SubstringAfter(".");
            var nsJSNamespace = new NestedString{
                Parent = ns,
                Header = (ns.LHS == "var ") ? path + " = (function () {" : path + " : (function () {",
                Footer = "}())",
                Comment = "RenderJS.JSNamespace",
            };
            var nsPackage =  new NestedString
            {
                Parent = nsJSNamespace,
                Header = "return {",
                Footer = "};",
                ChildSeparator = ",",
                ChildSeparatorFollowingLastElement = "",
                Comment = "RenderJS.Package",
            };
            #region Check If there are functions
            if (this.Functions != null && this.Functions.Count>0)
            {
                #region List Functions
                //var nsFunctions = new NestedString
                //{
                //    Parent = nsJSNamespace,
                //    Header = "return {",
                //    Footer = "};",
                //    ChildSeparator = ",",
                //    ChildSeparatorFollowingLastElement = "",
                //};
                this.Functions.DoForEach(fn =>
                {
                    #region Add Function
                    if(fn.ExplicitScript != null){
                        var nsFn = new NestedString{
                            Parent = nsPackage,
                            LHS = fn.FunctionName + ": ",
                            RHS = fn.ExplicitScript,
                            Comment = "RenderJS.ExplicitFn",
                        };
                    }else{
                        string header = fn.FunctionName + ": function (";
                        
                        var nsFn = new NestedString{
                            Parent = nsPackage,
                            //Header = header,
                            Footer = "}",
                            Comment = "RenderJS.nsFn",
                            //IsFunctionDefinition = true,
                        };
                        fn.Process(nsFn);
                        if (fn.ArgNames != null)
                        {
                            header += string.Join(", ", fn.ArgNames.ToArray());
                        }
                        header += ") {";
                        nsFn.Header = header;
                    }
                    #endregion
                });
                #endregion
            }
            #endregion
            this.Children.DoForEach(child =>
            {
                child.RenderJS(nsPackage);
                //RenderJS(nsJSNamespace);
            });
            
            
            
        }

    }

}
